#include "vars.h"

extern int count, exit_on_tcpconn; /* packet counter */
extern float narp, nrarp, nip, ntcp, nudp;

/*
 * app name/banner
 */
void
print_app_banner(void) {

    printf("%s - %s\n", APP_NAME, APP_DESC);
    printf("%s\n", APP_COPYRIGHT);
    printf("%s\n", APP_DISCLAIMER);
    printf("\n");

    return;
}

/*
 * print help text
 */
void
print_app_usage(void) {

    printf("Usage: %s -options [args]\n", "libpcap");
    printf("\n");
    printf("Options:\n");
    printf("\t--help\t\t-h\t\t\tListen on <interface> for packets.\n");
    printf("\t\t\t-s\t\t\tShow statics.\n");
    printf("\t\t\t-e\t\t\tExit application at first finalized two TCP connections.\n");
    printf("\t\t\t-n number_of_packets\tSet number of packets to be captured.\n");
    printf("\t--payload\t-p\t\tPrint the packets payload\n");
    printf("\t--interface\t-i interface\t\tListen on <interface> for packets.\n");
    printf("\t--filter\t-f 'filter'\t\tApply <filter> for packets.\n");
    printf("\t--reader\t-r reader_file\t\tOpen <reader_file> for packets stored in it.\n");
    printf("\t--writer\t-w writer_file\t\tOpen <writer_file> to store packet on it.\n");

    printf("\n");

    return;
}

/*
 * print data in rows of 16 bytes: offset   hex   ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload, int len, int offset) {

    int i;
    int gap;
    const u_char *ch;

    /* offset */
    printf("%05d   ", offset);

    /* hex */
    ch = payload;
    for (i = 0; i < len; i++) {
        printf("%02x ", *ch);
        ch++;
        /* print extra space after 8th byte for visual aid */
        if (i == 7)
            printf(" ");
    }
    /* print space to handle line less than 8 bytes */
    if (len < 8)
        printf(" ");

    /* fill hex gap with spaces if not full line */
    if (len < 16) {
        gap = 16 - len;
        for (i = 0; i < gap; i++) {
            printf("   ");
        }
    }
    printf("   ");

    /* ascii (if printable) */
    ch = payload;
    for (i = 0; i < len; i++) {
        if (isprint(*ch))
            printf("%c", *ch);
        else
            printf(".");
        ch++;
    }

    printf("\n");

    return;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void
print_payload(const u_char *payload, int len) {

    int len_rem = len;
    int line_width = 16; /* number of bytes per line */
    int line_len;
    int offset = 0; /* zero-based offset counter */
    const u_char *ch = payload;

    if (len <= 0)
        return;

    /* data fits on one line */
    if (len <= line_width) {
        print_hex_ascii_line(ch, len, offset);
        return;
    }

    /* data spans multiple lines */
    for (;;) {
        /* compute current line length */
        line_len = line_width % len_rem;
        /* print line */
        print_hex_ascii_line(ch, line_len, offset);
        /* compute total remaining */
        len_rem = len_rem - line_len;
        /* shift pointer to remaining bytes to print */
        ch = ch + line_len;
        /* add offset */
        offset = offset + line_width;
        /* check if we have line width chars or less */
        if (len_rem <= line_width) {
            /* print last line and get out */
            print_hex_ascii_line(ch, len_rem, offset);
            break;
        }
    }

    return;
}

char* convert(char n, char * output) {
    int i, j, lenght = sizeof (char) *8;
    char my_output[lenght + 1];
    my_output[lenght] = '\0';
    for (i = lenght - 1, j = 0; i >= 0; i--, j++)
        sprintf((char *) & my_output[j], "%c", (n & (1 << i)) ? '1' : '0');
    return strcpy(output, my_output);
}

char * get_tcp_flags(char * flags) {
    char * flags_char = malloc(sizeof (char));
    flags_char[0] = '\0';
    int i;

    for (i = 2; i < 8; i++) {
        if (flags[i] == '1') {
            if (strlen(flags_char) != 0) {
                flags_char = realloc(flags_char, strlen(flags_char) + 2);
                strcat(flags_char, "-");
            }
            flags_char = realloc(flags_char, strlen(flags_char) + 6);
            switch (i) {
                case 2:
                    strcat(flags_char, "[URG]");
                    break;
                case 3:
                    strcat(flags_char, "[ACK]");
                    break;
                case 4:
                    strcat(flags_char, "[PSH]");
                    break;
                case 5:
                    strcat(flags_char, "[RST]");
                    break;
                case 6:
                    strcat(flags_char, "[SYN]");
                    break;
                case 7:
                    strcat(flags_char, "[FIN]");
                    break;
            }
        }
    }
    return flags_char;
}

char * get_tcpconn_id(sniff_ip * ip, sniff_tcp * tcp, int reverse, char * output) {
    char aux_output[42], src[16], dst[16];
    bzero(aux_output, 42);
    strcpy(src, inet_ntoa(ip->ip_src));
    strcpy(dst, inet_ntoa(ip->ip_dst));
    if (reverse == 0)
        sprintf(aux_output, "%s%i%s%i", src, ntohs(tcp->th_sport), dst, ntohs(tcp->th_dport));
    else
        sprintf(aux_output, "%s%i%s%i", dst, ntohs(tcp->th_dport), src, ntohs(tcp->th_sport));
    strcpy(output, aux_output);
    //printf("============ %s\n", output);
    return output;
}

int print_tcp_header(sniff_tcp *tcp) {
    int size_tcp = TH_OFF(tcp)*4;
    if (size_tcp < 20) {
        printf("\t* Invalid TCP header length: %u bytes\n", size_tcp);
        return -1;
    }

    char * flags_output = malloc(sizeof (char) *9);

    printf("\t\tPuerto origen: %d\n", ntohs(tcp->th_sport));
    printf("\t\tPuerto destino: %d\n", ntohs(tcp->th_dport));
    printf("\t\tNúmero de secuencia: %d\n", ntohs(tcp->th_seq));
    printf("\t\tNúmero de reconocimiento: %d\n", ntohs(tcp->th_ack));
    printf("\t\tVentana de recepción: %d\n", ntohs(tcp->th_win));
    printf("\t\tFlags: %s\n", get_tcp_flags(convert(tcp->th_flags, flags_output)));
    printf("\t\tSuma de comprobación: %d\n", ntohs(tcp->th_sum));
    printf("\t\tLongitud de cabecera: %d\n", size_tcp);
    printf("\t\tDatos urgentes: %d\n", ntohs(tcp->th_urp));

    struct sniff_ip* ip;
    ip = (struct sniff_ip *) (((u_char *) tcp) - 20);
    char * conn_id1 = malloc(sizeof (char) *42);
    char * conn_id2 = malloc(sizeof (char) *42);

    // recocemos las dos primeras conexiones tcp en comenzar y finalizar correctamente
    switch (tcp->th_flags) {
        // paquete con SYN
        case 2:
            if (conn1->state == 0) {
                strcpy(conn1->id, get_tcpconn_id(ip, tcp, 0, conn_id1));
                conn1->state++;
                printf("SOLICITANDO CONEXION TCP Nº 1\n");
            } else
                if (conn2->state == 0) {
                strcpy(conn2->id, get_tcpconn_id(ip, tcp, 0, conn_id2));
                conn2->state++;
                printf("SOLICITANDO CONEXION TCP Nº 2\n");
            }
            break;
            // paquete con SYN - ACK
        case 18:
            if (conn1->state == 1) {
                if (strcmp(conn1->id, get_tcpconn_id(ip, tcp, 1, conn_id1)) == 0) {
                    conn1->state++;
                    conn1->th_seq = ntohs(tcp->th_seq);
                    printf("ESTABLECIENDO CONEXION TCP Nº 1\n");
                }
            } else
                if (conn2->state == 1) {
                if (strcmp(conn2->id, get_tcpconn_id(ip, tcp, 1, conn_id2)) == 0) {
                    conn2->state++;
                    conn2->th_seq = ntohs(tcp->th_seq);
                    printf("ESTABLECIENDO CONEXION TCP Nº 2\n");
                }
            }
            break;
            // paquete con ACK que confirma apertura de conexion
        case 16:
            if (conn1->state == 2
                    && strcmp(conn1->id, get_tcpconn_id(ip, tcp, 0, conn_id1)) == 0
                    && conn1->th_seq == ntohs(tcp->th_ack)) {
                conn1->state++;
                printf("CONEXIÓN TCP Nº 1 ESTABLECIDA\n");
            } else
                if (conn2->state == 2
                    && strcmp(conn2->id, get_tcpconn_id(ip, tcp, 0, conn_id2)) == 0
                    && conn2->th_seq == ntohs(tcp->th_ack)) {
                conn2->state++;
                printf("CONEXIÓN TCP Nº 2 ESTABLECIDA\n");
            }

            if (conn1->state == 4
                    && strcmp(conn1->id, get_tcpconn_id(ip, tcp, 0, conn_id1)) == 0
                    && conn1->th_seq == ntohs(tcp->th_ack)) {
                conn1->state++;
                printf("MEDIA CONEXIÓN TCP Nº 1 CERRADA\n");
            } else
                if (conn2->state == 4
                    && strcmp(conn2->id, get_tcpconn_id(ip, tcp, 0, conn_id2)) == 0
                    && conn2->th_seq == ntohs(tcp->th_ack)) {
                conn2->state++;
                printf("MEDIA CONEXIÓN TCP Nº 2 CERRADA\n");
            }

            if (conn1->state == 6
                    && strcmp(conn1->id, get_tcpconn_id(ip, tcp, 1, conn_id1)) == 0
                    && conn1->th_seq == ntohs(tcp->th_ack)) {
                conn1->state++;
                printf("CONEXIÓN TCP Nº 1 CERRADA\n");
            } else
                if (conn2->state == 6
                    && strcmp(conn2->id, get_tcpconn_id(ip, tcp, 1, conn_id2)) == 0
                    && conn2->th_seq == ntohs(tcp->th_ack)) {
                conn2->state++;
                printf("CONEXIÓN TCP Nº 2 CERRADA\n");
                if (exit_on_tcpconn != 0)
                    exit(1);
            }
            break;
        case 17:
        case 1:
            if (conn1->state == 3) {
                if (strcmp(conn1->id, get_tcpconn_id(ip, tcp, 0, conn_id1)) == 0) {
                    conn1->state++;
                    conn1->th_seq = ntohs(tcp->th_seq);
                } else
                    if (strcmp(conn1->id, get_tcpconn_id(ip, tcp, 1, conn_id1)) == 0) {
                    conn1->state++;
                    conn1->th_seq = ntohs(tcp->th_seq);
                    strcpy(conn1->id, conn_id1);
                }
                if (conn1->state == 4)
                    printf("CERRANDO CONEXIÓN TCP Nº 1 \n");
            } else
                if (conn2->state == 3) {
                if (strcmp(conn2->id, get_tcpconn_id(ip, tcp, 0, conn_id2)) == 0) {
                    conn2->state++;
                    conn2->th_seq = ntohs(tcp->th_seq);
                } else
                    if (strcmp(conn2->id, get_tcpconn_id(ip, tcp, 1, conn_id2)) == 0) {
                    conn2->state++;
                    conn2->th_seq = ntohs(tcp->th_seq);
                    strcpy(conn2->id, conn_id2);
                }
                if (conn2->state == 4)
                    printf("CERRANDO CONEXIÓN TCP Nº 2 \n");
            }
            if (conn1->state == 5) {
                printf("--- %s\n", get_tcpconn_id(ip, tcp, 0, conn_id1));
                if (strcmp(conn1->id, get_tcpconn_id(ip, tcp, 0, conn_id1)) == 0) {
                    conn1->state++;
                    conn1->th_seq = ntohs(tcp->th_seq);
                    strcpy(conn1->id, conn_id1);
                }
                if (conn1->state == 6)
                    printf("CERRANDO 2DA MITAD DE CONEXIÓN TCP Nº 1 \n");
            } else
                if (conn2->state == 5) {
                if (strcmp(conn2->id, get_tcpconn_id(ip, tcp, 0, conn_id2)) == 0) {
                    conn2->state++;
                    conn2->th_seq = ntohs(tcp->th_seq);
                    strcpy(conn2->id, conn_id2);
                }
                if (conn2->state == 6)
                    printf("CERRANDO 2DA MITAD DE CONEXIÓN TCP Nº 2 \n");
            }

            break;
    }
    free(flags_output);
    return size_tcp;
}

int print_udp_header(sniff_udp * udp) {
    int size_udp = ntohs(udp->th_offx2);
    if (size_udp < 8) {
        printf("\t* Invalid UDP segment length: %u bytes\n", size_udp);
        return -1;
    }

    printf("\t\tPuerto origen: %d\n", ntohs(udp->th_sport));
    printf("\t\tPuerto destino: %d\n", ntohs(udp->th_dport));
    printf("\t\tSuma de comprobación: %d\n", ntohs(udp->th_sum));
    printf("\t\tLongitud del segmento: %d\n", ntohs(udp->th_offx2));

    return 8;
}

void
print_statics(void) {
    printf("\nESTADÍSTICAS\n");
    printf("------------\n");
    printf("RED:\n");
    printf("\tIP: %.2f %\n", nip * 100 / (count - 1));
    printf("\tARP: %.2f %\n", narp * 100 / (count - 1));
    printf("\tRARP: %.2f %\n", nrarp * 100 / (count - 1));
    printf("TRANSPORTE:\n");
    printf("\tTCP: %.2f %\n", ntcp * 100 / (count - 1));
    printf("\tUDP: %.2f %\n", nudp * 100 / (count - 1));
    printf("\tOTROS: %.2f %\n", (count - 1 - ntcp - nudp)*100 / (count - 1));
}
